home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / clang / mcomm600.zip / SMALTERM.CPP < prev    next >
C/C++ Source or Header  |  1994-10-03  |  60KB  |  1,578 lines

  1.  
  2. /////////////////////////////////////////////////////////////////////////////
  3. //     S M A L T E R M  --  D e m o   D r i v e r   f o r   M C O M M      //
  4. //                         A s y n c   R o u t i n e s                     //
  5. //                                                                         //
  6. //                                                                         //
  7. //             Mike Dumdei, 6 Holly Lane, Texarkana TX 75503               //
  8. //                 North East Texas DataLink, 903 838-6713                 //
  9. //                                                                         //
  10. //              (Link with comm_s.lib for external functions)              //
  11. //                     ztc smalterm.cpp comm.cpp comm_s.lib                //
  12. //                     bcc smalterm.cpp comm.cpp comm_s.lib                //
  13. //                     tcc smalterm.cpp comm.cpp comm_s.lib                //
  14. //                     cl smalterm.cpp comm.cpp /link comm_s               //
  15. /////////////////////////////////////////////////////////////////////////////
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <ctype.h>
  19. #include <string.h>
  20. #include <process.h>
  21. #include <stdarg.h>
  22. #include <dos.h>
  23. #include <bios.h>
  24. #include <signal.h>
  25.  
  26. #include "comm.hpp"                 // header for AsyncPort class
  27. #include "timer.hpp"                // header for Timer class
  28. #include "ansivid.hpp"              // header for ANSI display functions
  29. #define KEY_INT
  30. #include "keys.h"                   // key definition header
  31. #include "colors.h"                 // vid mode & color definition header
  32. #include "extra.h"                  // extra functions header
  33.  
  34. #if defined(__TURBOC__)             // Turbo C++
  35.   #define KBHIT   bioskey(1)
  36.   #define KBREAD  bioskey(0)
  37. #else                               // Zortech C++, Microsoft C++
  38.   #define KBHIT   _bios_keybrd(1)
  39.   #define KBREAD  _bios_keybrd(0)
  40. #endif
  41.  
  42. #ifndef __ZTC__
  43.   #include <io.h>
  44.   #define strcmpl stricmp
  45. #endif
  46.  
  47. #define NO_INPUT     (-1)
  48. #define ESC_PRESSED  (-2)
  49. #define TIMED_OUT    (-3)
  50. #define NO_CARRIER   (-4)
  51. #define ERROR_LIMIT  (-5)
  52. #define GOT_CANNED   (-6)
  53. #define BLK_COMP_ERR (-7)
  54. #define BLK_SEQ_ERR  (-8)
  55. #define CHECKSUM_ERR (-9)
  56.  
  57. #define TERMINAL       0
  58. #define HOSTMODE       1            // hostmode isn't in here yet
  59.  
  60. #define SOH         '\x01'
  61. #define STX         '\x02'
  62. #define EOT         '\x04'
  63. #define ACK         '\x06'
  64. #define NAK         '\x15'
  65. #define CAN         '\x18'
  66.  
  67. ///////////////////////////////////////////////////////////////
  68. //  function declarations in order found in source (random)  //
  69. //:////////////////////////////////////////////////////////////
  70. // void ControlBreakHandler(int sig);
  71. int ProcessKeyPress();
  72. void ProcessExtendedKey(int LongCh);
  73. int ProcessAsyncChar();
  74. int SaveScreen();
  75. void RestoreScreen();
  76. void VideoStatus(int saving);
  77. void ClearScreen();
  78. void ToggleLogFile();
  79. void ToggleEcho();
  80. void ToggleLineFeeds();
  81. void ToggleDtr();
  82. void DialNumber();
  83. void DOS_Shell();
  84. void DOS_Command();
  85. void HostMode();
  86. void MessageUpload();
  87. void TXZMUpload();
  88. void TXZMDownload();
  89. void RunBatchFile(char *FileName);
  90. void SendFile();
  91. void ReceiveFile();
  92. void ExitProgram();
  93. int DisplayHelp();
  94. void DisplayParameters();
  95. void ChangeParameters();
  96. int DrawBox(int Top, int Lft, int Height, int Length);
  97. void RemoveBox();
  98. int Prompt(char *Response, char *PromptMsg, int MaxResponse);
  99. void HangUp();
  100. int MonitoredDelay(int Ticks, int MonitorCarrier);
  101. int WaitFor(char *String, int Ticks, int MonitorCarrier);
  102. void vDisplay(char *Format, ...);
  103. void vDisplay(int Color, char *Format, ...);
  104. void vDisplay(int Row, int Col, char *Format, ...);
  105. int PushScreen(int Row, int Col, int NbrRows, int NbrCols);
  106. int PopScreen();
  107. void ReceiveXmodem(char *FileName);
  108. void SendXmodem(char *FileName, int BlockSize = 128);
  109. void XmodemExitMsg(int rval);
  110. int RxWithTimeOut(int Ticks);
  111. void SetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params);
  112. void ResetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params);
  113. void SendCANs();
  114. int WaitForBlockToEnd(int Ticks);
  115. int GetHdrChar(int Ticks);
  116. void TransferWindow(int Flag);
  117.  
  118. ////////////////////////
  119. //  global variables  //
  120. ////////////////////////
  121. AsyncPort port(4096, 2048);     // port with 4K transmit, 2K receive buffers
  122. char port_name[5];              // ASCII "COM1" or "COM2"
  123. int port_number;                // assigned number COM1==0, COM2==1
  124. int Mode;                       // identifies to subroutines HOST or TERMINAL
  125. FILE *LogFile = NULL;           // handle for log file
  126. char DialString[40];            // storage for dial string
  127. int LineFeeds = 0;              // supply LineFeeds flag
  128. int Echo = 0;                   // Echo on flag
  129. int Dtr = 1;                    // DTR signal level flag
  130. int cyn, hred, grn, hgrn;       // video color variables to enable support
  131. int wht, hblu, ylw;             //   for either color or mono system
  132. char vDispBuf[128];             // buffer for formatted display functions
  133. char *ScreensArray[10];         // array of pointers for Push/Pop Screen
  134. char ScreenIndex = 0;           // index of current pointer in above array
  135.  
  136.  
  137. /////////////////////////////////////////////////
  138. //  main                                       //
  139. //://////////////////////////////////////////////
  140. int cdecl main (int argc, char *argv[])
  141. {
  142.     char params[10], *pc;
  143.     int got_port = 0, return_value, i;
  144.  
  145.     ////////////////////////////////////
  146.     // process command line arguments //
  147.     ////////////////////////////////////
  148.     *params = '\0';                             // preset params to nul string
  149.     for (i = 1; i < argc; i++)
  150.     {
  151.         pc = argv[i];                           // pointer to argument
  152.         if (*pc == '-' || *pc == '/')
  153.             ++pc;                               // skip any switch characters
  154.         if (got_port == 0)
  155.         {
  156.             if (strcmpl(argv[i], "COM1") == 0)      // check for "COM1"
  157.                 got_port = 1;
  158.             else if (strcmpl(argv[i], "COM2") == 0) // check for "COM2"
  159.                 got_port = 1, port_number = COM2;
  160.         }
  161.         if (isdigit(*pc) && strlen(pc) < 10)    // check for parameter string
  162.             strupr(strcpy(params, argv[i]));
  163.     }
  164.     strcpy(port_name, (port_number == COM1) ? "COM1" : "COM2");
  165.  
  166.     ///////////////////////////
  167.     // initialize the video  //
  168.     ///////////////////////////
  169.     initvid();                          // initialize lowlevel video variables
  170.     if (v_mode != MONO && v_mode != CO80)
  171.         setvmode(CO80);                 // reset video mode if necessary
  172.     if (v_mode == CO80)
  173.     {                                   // attributes to use if color system
  174.         cyn = CYN, hred = H_RED, grn = GRN, hgrn = H_GRN,
  175.         wht = WHT, hblu = H_BLU, ylw = YLW;
  176.     }
  177.     else
  178.     {                                   // attributes to use if mono system
  179.         cyn = WHT, hred = H_WHT, grn = WHT, hgrn = H_WHT,
  180.         wht = WHT, hblu = H_WHT, ylw = H_WHT;
  181.     }
  182.     SaveScreen();
  183.  
  184.     /////////////////////////////
  185.     // display sign on message //
  186.     /////////////////////////////
  187.     Display(hred, "SMALTERM - Sample driver for MCOMM library functions\n");
  188.     Display(grn, "    Mike Dumdei, 6 Holly Lane, Texarkana TX 75503\n");
  189.     Display("    North East Texas DataLink   903 838-6713\n");
  190.     Display(hred, "Usage: smalterm {COM1 | COM2} {param str EX: 2400N81}\n\n");
  191.     v_color = cyn;
  192.  
  193.     //////////////////////////
  194.     // open the serial port //
  195.     //////////////////////////
  196.     return_value = port.Open(port_number, params);
  197.     if (return_value != 0)                      // if port open failed
  198.     {
  199.         vDisplay(
  200.           "SMALTERM: Can't open %s, Parameters: %s, Error code: %04X\n",
  201.            port_name, (*params) ? params : "default", return_value);
  202.         exit(return_value);
  203.     }                                           // if port open succeeded
  204.     vDisplay("SMALTERM: %s -- PORT OPENED\n\n", port_name);
  205.     Display("Press F1 for HELP screen\n");
  206.  
  207.     //////////////////////////
  208.     // miscellaneous set up //
  209.     //////////////////////////
  210.     tickhookset(1);                     // enable master tick in Timer class
  211. /*  ControlBreakHandler(SIGINT);        // initialize control-break handler */
  212.     SetWindowSize(0, 0, 23, 79);        // set window to protect status line
  213.     Fill(24, 0, '═', cyn, 80);          // display status line
  214.     Display(24, 60, hgrn, port_name);
  215.     DisplayParameters();
  216.     Display(24, 10, hgrn, "DTR");
  217.     *DialString = '\0';                 // clear out dial string
  218.     Mode = TERMINAL;                    // terminal, e.g. not host mode
  219.  
  220.     ///////////////////////////////////
  221. //////       MAIN PROGRAM LOOP       //////
  222.     ///////////////////////////////////
  223.     while (1)
  224.     {
  225.         if (KBHIT)                      // process keyboard character if a
  226.             ProcessKeyPress();          //  key was pressed
  227.         if (port.RxLevel() != 0)        // process character from serial
  228.             ProcessAsyncChar();         //  port if one is available
  229.     }
  230. #if (!__TURBOC__ && !__ZTC__)
  231.     return 0;                           // suppresses MSC compile warning
  232. #endif
  233. }
  234.  
  235. /*
  236. /////////////////////////////////////////////////
  237. //  ControlBreakHandler                        //
  238. //://////////////////////////////////////////////
  239. void ControlBreakHandler(int sig)
  240. {
  241.     signal(SIGINT, ControlBreakHandler);
  242. }
  243. */
  244.  
  245. /////////////////////////////////////////////////
  246. //  ProcessKeyPress                            //
  247. //://////////////////////////////////////////////
  248. int ProcessKeyPress()
  249. {
  250.     int LongCh = KBREAD;                // scan code + character
  251.     char Ch = (char)LongCh;             // character without scan code
  252.  
  253.     if (Ch != '\0')                     // if normal key
  254.     {
  255.         port.Tx(Ch);                    //   send it out the serial port
  256.         if (Echo && Ch != ESC)          //   if echo on & not ESC (don't want
  257.         {                               //    to start an ANSI sequence),
  258.             VideoStatus(1);             //   save video status
  259.             if (Ch == '\r' && LineFeeds)
  260.                 Ch = '\n';              //   make it a LF if CR & LF toggle
  261.             Display(Ch);                //    display the char,
  262.             if (LogFile)                //    and write it the log file if
  263.                 fputc(Ch, LogFile);     //    its open
  264.             VideoStatus(0);             //   restore video status
  265.         }
  266.     }
  267.     else                                // extended key, e.g. F1, ALT_X, etc,
  268.         ProcessExtendedKey(LongCh);
  269.     return (LongCh);                    // return the key pressed
  270. }
  271.  
  272. /////////////////////////////////////////////////
  273. //  ProcessExtendedKey                         //
  274. //://////////////////////////////////////////////
  275. void ProcessExtendedKey(int LongCh)
  276. {
  277.     static struct JUMP_TABLE
  278.     {
  279.         int KeyCode;
  280.         void (*Function)();
  281.     }
  282.       TERMINAL_Table[] = {
  283.         { ALT_C, ClearScreen },     { ALT_E, ToggleEcho },
  284.         { ALT_X, ExitProgram },     { ALT_S, DOS_Shell },
  285.         { ALT_D, DialNumber },      { PGUP, SendFile },
  286.         { PGDN, ReceiveFile },      { ALT_H, HangUp },
  287.         { ALT_L, ToggleLineFeeds }, { F2, ToggleLogFile },
  288.         { F10, HostMode },          { ALT_P, ChangeParameters },
  289.         { F4, DOS_Command },        { F3, MessageUpload },
  290.         { F5, TXZMUpload },         { F6, TXZMDownload },
  291.         { 0, NULL } },
  292.       HOSTMODE_Table[] = {
  293.         { ALT_C, ClearScreen },     { ALT_E, ToggleEcho },
  294.         { ALT_S, DOS_Shell },       { ALT_H, HangUp },
  295.         { ALT_L, ToggleLineFeeds }, { F2, ToggleLogFile },
  296.         { ALT_P, ChangeParameters },{ F4, DOS_Command },
  297.         { F3, MessageUpload },      { F5, TXZMUpload },
  298.         { F6, TXZMDownload },       { 0, NULL } },
  299.       *JumpTableList[] = { TERMINAL_Table, HOSTMODE_Table };
  300.  
  301.     JUMP_TABLE *pjt = JumpTableList[Mode];
  302.  
  303.     if (LongCh == F1)
  304.         LongCh = DisplayHelp();         // run Help if F1 was pressed
  305.     while (pjt->KeyCode != LongCh && pjt->KeyCode)
  306.         ++pjt;                          // look up the key in the table
  307.     if (pjt->KeyCode)
  308.         pjt->Function();                // if key found, run the function
  309. }
  310.  
  311. /////////////////////////////////////////////////
  312. //  ProcessAsyncChar                           //
  313. //://////////////////////////////////////////////
  314. int ProcessAsyncChar()
  315. {
  316.     static int ToggleHost = 0;
  317.  
  318.     char Ch = (char)port.Rx();          // read the character
  319.     if (Ch == '\r' && LineFeeds)
  320.         Ch = '\n';                      // translate to LF if supplying LF's
  321.     if (Ch != '~')                      // using ~ to remotely toggle host
  322.         ToggleHost = 0;                 //  mode, reset counter if not ~
  323.     else
  324.     {
  325.         if (++ToggleHost >= 10)         // seq of 10 ~'s toggles host mode
  326.         {
  327.             HostMode();
  328.             ToggleHost = 0;             // reset counter on HostMode exit
  329.             return 0;
  330.         }
  331.     }
  332.     Display(Ch);                        // display the character
  333.     if (LogFile && v_ansiseq == '\0')
  334.         fputc(Ch, LogFile);             // write to log file if its on
  335.     return ((int)Ch & 0xff);            // return received char
  336. }
  337.  
  338. /////////////////////////////////////////////////
  339. //  SaveScreen                                 //
  340. //://////////////////////////////////////////////
  341. int SaveScreen()
  342. {
  343.     if (PushScreen(0, 0, 25, 80) != 0)  // move screen to memory buffer
  344.         return 0;                       //  return failed if no memory
  345.     VideoStatus(1);                     // save video color & ANSI status
  346.     SetWindowSize(0, 0, 24, 79);
  347.     v_color = wht;
  348.     ClearWindow();                      // reset window size, clear screen
  349.     return 1;                           // return success
  350. }
  351.  
  352. /////////////////////////////////////////////////
  353. //  RestoreScreen                              //
  354. //://////////////////////////////////////////////
  355. void RestoreScreen()
  356. {
  357.     PopScreen();                        // restore screen
  358.     VideoStatus(0);                     // restore v_color & v_ansiseq
  359.     SetWindowSize(0, 0, 23, 79);        // reset the window size
  360. }
  361.  
  362. /////////////////////////////////////////////////
  363. //  VideoStatus                                //
  364. //://////////////////////////////////////////////
  365. void VideoStatus(int saving)
  366. {
  367.     static char savedcolor[4], savedseq[4], vsi = 0;
  368.  
  369.     if (saving)
  370.         savedcolor[vsi] = v_color, savedseq[vsi++] = v_ansiseq,
  371.         v_ansiseq = '\0';
  372.     else
  373.         v_color = savedcolor[--vsi], v_ansiseq = savedseq[vsi];
  374. }
  375.  
  376. /////////////////////////////////////////////////
  377. //  ClearScreen                                //
  378. //://////////////////////////////////////////////
  379. void ClearScreen()
  380. {
  381.     v_color = cyn;                      // reset screen color
  382.     SetWindowSize(0, 0, 23, 79);        // reset the window size
  383.     ClearWindow();                      // clear the current window
  384. }
  385.  
  386. /////////////////////////////////////////////////
  387. //  ToggleLogFile                              //
  388. //://////////////////////////////////////////////
  389. void ToggleLogFile()
  390. {
  391.     if (LogFile == NULL)                // if it's off, turn it on
  392.     {
  393.         if ((LogFile = fopen("ST.LOG", "ab")) != NULL)
  394.             Display(24, 25, hgrn, "LOG");
  395.     }
  396.     else                                // if it's on, turn it off
  397.     {
  398.         fclose(LogFile);
  399.         LogFile = NULL;                 // a NULL FILE * shows log is off
  400.         Display(24, 25, cyn, "═══");
  401.     }
  402. }
  403.  
  404. /////////////////////////////////////////////////
  405. //  ToggleEcho                                 //
  406. //://////////////////////////////////////////////
  407. void ToggleEcho()
  408. {
  409.     Echo ^= 1;                          // switch state, update status line
  410.     Display(24, 15, (Echo) ? hgrn : cyn, (Echo) ? "ECHO" : "════");
  411. }
  412.  
  413. /////////////////////////////////////////////////
  414. //  ToggleLineFeeds                            //
  415. //://////////////////////////////////////////////
  416. void ToggleLineFeeds()
  417. {
  418.     LineFeeds ^= 1;                     // switch state, update status line
  419.     Display(24, 21, (LineFeeds) ? hgrn : cyn, (LineFeeds) ? "LF" : "══");
  420. }
  421.  
  422. /////////////////////////////////////////////////
  423. //  ToggleDtr                                  //
  424. //://////////////////////////////////////////////
  425. void ToggleDtr()
  426. {
  427.     Dtr ^= 1;                           // switch state, update status line
  428.     port.Dtr(Dtr);
  429.     Display(24, 10, (Dtr) ? hgrn : (hred | BLNK), "DTR");
  430. }
  431.  
  432. /////////////////////////////////////////////////
  433. //  DialNumber                                 //
  434. //://////////////////////////////////////////////
  435. void DialNumber()
  436. {
  437.     char Response[25], *pc = Response;
  438.  
  439.     if (Prompt(pc, "Enter number (p###=Pulse, r=Redial): ", 24) != 0)
  440.         return;                         // get number, exit if ESC pressed
  441.     if (isalpha(*pc))                   // look for 'P' or 'R'
  442.     {
  443.         *pc = toupper(*pc);
  444.         if (*pc == 'R' && *DialString != '\0')
  445.         {
  446.             port.Tx(DialString);        // redial command
  447.             return;
  448.         }
  449.         else if (*pc != 'P')
  450.             return;                     // invalid response
  451.     }
  452.     if (*pc == 'P')
  453.         sprintf(DialString, "%s%s\r", "ATDP", &pc[1]);
  454.     else
  455.         sprintf(DialString, "%s%s\r", "ATDT", pc);
  456.     port.Tx(DialString);
  457. }
  458.  
  459. /////////////////////////////////////////////////
  460. //  DOS_Shell                                  //
  461. //://////////////////////////////////////////////
  462. void DOS_Shell()
  463. {
  464.     char *comspec;
  465.  
  466.     comspec = getenv("COMSPEC");        // get command interpreter name
  467.     if (comspec == NULL)
  468.         comspec = "COMMAND.COM";
  469.     if (SaveScreen() == 0)              // save the current screen
  470.         return;                         //  exit if save failed
  471.     Display(hred, "Type EXIT to return to SMALTERM.");
  472.     spawnlp(0, comspec, comspec, NULL); // run command interpreter
  473.     RestoreScreen();                    // restore the original screen
  474. }
  475.  
  476. /////////////////////////////////////////////////
  477. //  DOS_Command                                //
  478. //://////////////////////////////////////////////
  479. void DOS_Command()
  480. {
  481.     char buf[48];
  482.  
  483.     if (Prompt(buf, "Enter DOS command: ", 46) != 0)
  484.         return;                         // get the command
  485.     if (SaveScreen() == 0)              // save the current screen
  486.         return;                         //  exit if save failed
  487.     system(buf);                        // execute command
  488.     RestoreScreen();                    // restore the original screen
  489. }
  490.  
  491. /////////////////////////////////////////////////
  492. //  HostMode                                   //
  493. //://////////////////////////////////////////////
  494. void HostMode()
  495. {
  496.     // do this later
  497. }
  498.  
  499. /////////////////////////////////////////////////
  500. //  MessageUpload                              //
  501. //://////////////////////////////////////////////
  502. void MessageUpload()
  503. {
  504.     char buf[81];
  505.     FILE *fh;
  506.     char *pc;
  507.  
  508.     if (Prompt(buf, "Enter ASCII filename: ", 46) != 0)
  509.         return;                         // get the file name
  510.     if ((fh = fopen(buf, "rt")) == NULL)
  511.     {
  512.         Prompt(buf, "File open error, Press ENTER\a", 0);
  513.         return;                         // abort if can't open the file
  514.     }
  515.     port.RxFlush();                     // flush the port
  516.     port.TxFlush();
  517.     while (fgets(buf, 80, fh))          // while more is left
  518.     {
  519.         if (*buf == '\n')               // change blank lines to SPC,CRs
  520.             strcpy(buf, " \r");
  521.         else if ((pc = strchr(buf, '\n')) != NULL)
  522.             *pc = '\r';                 // change LFs to simulate ENTER key
  523.         for (pc = buf; *pc; pc++)
  524.         {
  525.             while (!port.TxEmpty())     // send a char at a time for looks
  526.             {
  527.                 if (KBHIT && (KBREAD == X_ESC))
  528.                 {                       // monitor for upload aborted
  529.                     fclose(fh);
  530.                     return;
  531.                 }
  532.                 while (port.RxLevel() != 0)
  533.                     ProcessAsyncChar();
  534.             }
  535.             port.Tx(*pc);
  536.         }
  537.     }
  538.     fclose(fh);                         // close the file
  539. }
  540.  
  541. /////////////////////////////////////////////////
  542. //  TXZMUpload                                 //
  543. //://////////////////////////////////////////////
  544. void TXZMUpload()
  545. {
  546.     RunBatchFile("SND.BAT");
  547. }
  548.  
  549. /////////////////////////////////////////////////
  550. //  TXZMDownload                               //
  551. //://////////////////////////////////////////////
  552. void TXZMDownload()
  553. {
  554.     RunBatchFile("RCV.BAT");
  555. }
  556.  
  557. /////////////////////////////////////////////////
  558. //  RunBatchFile                               //
  559. //://////////////////////////////////////////////
  560. void RunBatchFile(char *FileName)
  561. {
  562.     char lbuf[50], *pc = lbuf;
  563.  
  564.     strcpy(pc, FileName);
  565.     pc += strlen(pc);
  566.     *pc++ = ' ';                        // copy filename to local buffer
  567.     if (Prompt(pc, "Enter batch parameters -> ", 40) == ESC_PRESSED)
  568.         return;                         // add on any args, return if ESC
  569.     if (SaveScreen() == 0)              // save current screen
  570.         return;                         //  return if save failed
  571.     vDisplay(hred, "Executing: %s\n\n", lbuf);
  572.     system(lbuf);                       // run the batch file
  573.     RestoreScreen();                    // restore the screen
  574. }
  575.  
  576. /////////////////////////////////////////////////
  577. //  SendFile                                   //
  578. //://////////////////////////////////////////////
  579. void SendFile()
  580. {
  581.     char buf[48], *pc;
  582.  
  583.     if (Prompt(buf, "Enter Filename{,k}: ", 46) != 0)
  584.         return;                         // get the file name
  585.     pc = strchr(buf, ',');              // check if Xmodem-1k selected
  586.     if (pc && (strchr(pc, 'k') || strchr(pc, 'K')))
  587.     {
  588.         *pc = '\0';
  589.         SendXmodem(buf, 1024);
  590.     }
  591.     else                                // send the file
  592.         SendXmodem(buf);
  593. }
  594.  
  595. /////////////////////////////////////////////////
  596. //  ReceiveFile                                //
  597. //://////////////////////////////////////////////
  598. void ReceiveFile()
  599. {
  600.     char buf[48], *pc;
  601.  
  602.     if (Prompt(buf, "Enter Filename: ", 46) != 0)
  603.         return;                         // get the file name
  604.     ReceiveXmodem(buf);                 // receive the file
  605. }
  606.  
  607. /////////////////////////////////////////////////
  608. //  ExitProgram                                //
  609. //://////////////////////////////////////////////
  610. void ExitProgram()
  611. {
  612.     char buf[2];
  613.  
  614.     switch(Prompt(buf, "Exit SmalTerm (Y, n)? ", 1))
  615.     {                                   // make sure they want to do this
  616.         case ESC_PRESSED:
  617.             return;                     // return if ESC
  618.         default:
  619.             if (*buf != 'y' && *buf != 'Y')
  620.                 return;                 // if not 'Yes', then return
  621.         case NO_INPUT:                  // 'Enter key' is a default 'yes'
  622.             break;
  623.     }
  624.     RestoreScreen();                    // replace the original screen
  625.     tickhookset(0);                     // remove the interrupt 1C hook
  626.     exit(0);                            // leave the program
  627. }
  628.  
  629. /////////////////////////////////////////////////
  630. //  DisplayHelp                                //
  631. //://////////////////////////////////////////////
  632. int DisplayHelp()               // interim help function
  633. {
  634.     int LongCh;
  635.     static char *HelpScrn = "\
  636.     F1 - Help                   ALT C - Clear Screen\n\
  637.     F2 - Toggle Log File        ALT D - Dial Number\n\
  638.     F3 - BBS Message Upload     ALT E - Toggle Echo\n\
  639.     F4 - Execute DOS Command    ALT H - Hang Up\n\
  640.     F5 - TXZM Upload   SND.BAT  ALT L - Toggle Line Feeds\n\
  641.     F6 - TXZM Download RCV.BAT  ALT P - Change Parameters\n\
  642.    F10 - Host Mode (not there)  ALT S - DOS Shell\n\
  643.   PGUP - Xmodem Send            ALT X - Exit Program\n\
  644.   PGDN - Xmodem Receive  << Press ESC or Command >>";
  645.  
  646.     DrawBox(5, 10, 12, 60);
  647.     SetWindowSize(6, 11, 18, 69);
  648.     v_color = ylw;
  649.     Display(6, 12, "SMALTERM COMMANDS:\n");
  650.     Display(wht, HelpScrn);
  651.     LongCh = KBREAD;
  652.     RemoveBox();
  653.     SetWindowSize(0, 0, 23, 79);
  654.     return LongCh;
  655. }
  656.  
  657. /////////////////////////////////////////////////
  658. //  DisplayParameters                          //
  659. //://////////////////////////////////////////////
  660. void DisplayParameters()
  661. {
  662.     Fill(24, 65, '═', cyn, 10);
  663.     Display(24, 65, hgrn, port.Params());
  664. }
  665.  
  666. /////////////////////////////////////////////////
  667. //  ChangeParameters                           //
  668. //://////////////////////////////////////////////
  669. void ChangeParameters()
  670. {
  671.     char buf[12];
  672.  
  673.     if (Prompt(buf, "Enter parameters (Ex: 2400N81) -> ", 10) != 0)
  674.         return;                         // get the new parameters
  675.     if (port.Params(strupr(buf)) == 0)
  676.         DisplayParameters();            // display updated params if success,
  677.     else                                //  else display error message
  678.         Prompt(buf, "Invalid parameters, Press ENTER to continue\a", 0);
  679. }
  680.  
  681. /////////////////////////////////////////////////
  682. //  DrawBox                                    //
  683. //://////////////////////////////////////////////
  684. int DrawBox(int Top, int Lft, int Height, int Length)
  685. {
  686.     static char BoxChar[] = { '╔', '╗', '╚', '╝', '═', '║' };
  687.     int Btm, Rgt;
  688.  
  689.     if (PushScreen(Top, Lft, Height, Length) != 0)
  690.         return 0;                   // save area for box, return if failed
  691.     Btm = Top + Height - 1;
  692.     Rgt = Lft + Length - 1;
  693.     VideoStatus(1);                 // save video variables
  694.     v_color = wht;
  695.     SetWindowSize(Top, Lft, Btm, Rgt);
  696.     ClearWindow();                  // clear the area for the box
  697.     SetWindowSize(0, 0, 23, 79);
  698.     v_color = hblu;                 // draw the box
  699.     Fill(Top, Lft, BoxChar[4], hblu, Length);
  700.     Fill(Btm, Lft, BoxChar[4], hblu, Length);
  701.     Fill(Top, Rgt, BoxChar[5], hblu, Height, 0);
  702.     Fill(Top, Lft, BoxChar[5], hblu, Height, 0);
  703.     Display(Top, Lft, BoxChar[0]);
  704.     Display(Top, Rgt, BoxChar[1]);
  705.     Display(Btm, Lft, BoxChar[2]);
  706.     Display(Btm, Rgt, BoxChar[3]);
  707.     return 1;
  708. }
  709.  
  710. /////////////////////////////////////////////////
  711. //  RemoveBox                                  //
  712. //://////////////////////////////////////////////
  713. void RemoveBox()
  714. {
  715.     PopScreen();                        // restore screen
  716.     VideoStatus(0);                     // reset v_color & v_ansiseq
  717. }
  718.  
  719. /////////////////////////////////////////////////
  720. //  Prompt                                     //
  721. //://////////////////////////////////////////////
  722. int Prompt(char *Response, char *PromptMsg, int MaxResponse)
  723. {
  724.     int Lft, Length;
  725.     char *pc, *end, ch;
  726.  
  727.     Length = strlen(PromptMsg) + MaxResponse + 6;
  728.     Lft = (80 - Length) / 2;            // calculate box size required
  729.     if (DrawBox(9, Lft, 5, Length) == 0) // draw the box
  730.         return ESC_PRESSED;
  731.     v_color = hred;
  732.     Display(11, Lft + 3, PromptMsg);    // display prompt
  733.     v_color = wht;
  734.     pc = Response, end = pc + MaxResponse;
  735.  
  736.     while ((ch = (char)KBREAD) != '\r' && ch != ESC)
  737.     {                                   // get the response
  738.         if (ch == '\b')
  739.         {                               // backspace key
  740.             if (pc > Response)
  741.                 --pc, Display(ch);
  742.             continue;
  743.         }
  744.         if (pc != end && isprint((int)ch & 0xff))
  745.         {                               // good character
  746.             Display(ch), *pc++ = ch;
  747.             continue;
  748.         }
  749.         Display('\a');                  // illegal character
  750.     }
  751.     *pc = '\0';                         // terminate the string
  752.     RemoveBox();
  753.     if (ch == ESC)
  754.         return ESC_PRESSED;             // return something
  755.     if (*Response == '\0')
  756.         return NO_INPUT;
  757.     return 0;
  758. }
  759.  
  760. /////////////////////////////////////////////////
  761. //  HangUp                                     //
  762. //://////////////////////////////////////////////
  763. void HangUp()
  764. {
  765.     ToggleDtr();                        // drop DTR
  766.     port.TxFlush();
  767.     port.RxFlush();                     // flush the buffers
  768.     if (MonitoredDelay(24, 1) == 0)     // wait a while for carrier
  769.     {                                   //  to drop
  770.         port.Tx("+++");                 // if dropping Dtr didn't
  771.         if (MonitoredDelay(18, 1) == 0) //  work try modem command
  772.         {
  773.             port.Tx("ATH0\r");
  774.             WaitFor("OK", 54, 1);       // 3 secs, monitor carrier
  775.         }
  776.     }
  777.     ToggleDtr();
  778. }
  779.  
  780. /////////////////////////////////////////////////
  781. //  MonitoredDelay                             //
  782. //://////////////////////////////////////////////
  783. int MonitoredDelay(int Ticks, int MonitorCarrier)
  784. {
  785.     int LongCh;
  786.  
  787.     Timer t(Ticks);                     // start the delay
  788.     while (!t.Expired())
  789.     {
  790.         if ((LongCh = KBHIT) != 0)      // check the keyboard
  791.         {
  792.             if (LongCh == X_ESC)
  793.             {
  794.                 KBREAD;
  795.                 return ESC_PRESSED;     // return if ESC pressed
  796.             }
  797.             ProcessKeyPress();          // process key if not ESC
  798.         }
  799.         if (MonitorCarrier && !port.Carrier())
  800.             return NO_CARRIER;
  801.         if (port.RxLevel() != 0)        // check the async port
  802.             ProcessAsyncChar();
  803.     }
  804.     return 0;
  805. }
  806.  
  807. /////////////////////////////////////////////////
  808. //  WaitFor                                    //
  809. //://////////////////////////////////////////////
  810. int WaitFor(char *String, int Ticks, int MonitorCarrier)
  811. {
  812.     char ch, *pc, *buf, *end;
  813.     int MatchLen, LongCh, rval;
  814.  
  815.     if ((MatchLen = strlen(String)) == 0)
  816.         return (0);                     // return if nothing to wait for
  817.     buf = new char[MatchLen];           // allocate & clear incoming chars buf
  818.     memset(buf, '\0', MatchLen);
  819.     pc = buf - 1;
  820.     end = pc + MatchLen;                // preset pointers
  821.  
  822.     Timer t(Ticks);                     // start a timer for 'Ticks' duration
  823.     while (1)
  824.     {
  825.         while (port.RxLevel() != 0)     // check for characters in the port
  826.         {
  827.             ch = ProcessAsyncChar();
  828.             if (pc != end)
  829.             {                           // if don't have MatchLen chars yet
  830.                 *++pc = ch;
  831.                 if (pc != end)
  832.                     continue;
  833.             }
  834.             else                        // have MatchLen chars
  835.             {
  836.                 memmove(buf, &buf[1], MatchLen);
  837.                 *pc = ch;               // put this char in the match buffer
  838.             }                           //  and check for a match
  839.             if (*buf == *String && !memicmp(buf, String, MatchLen))
  840.             {
  841.                 delete buf;             // if found wait for string
  842.                 return 0;
  843.             }
  844.         }
  845.         if ((LongCh = KBHIT) != 0)      // if got a keyboard entry
  846.         {
  847.             if (LongCh == X_ESC)
  848.             {
  849.                 KBREAD;
  850.                 rval = ESC_PRESSED;     // return if it was ESC
  851.                 break;
  852.             }
  853.             ProcessKeyPress();          //  else process key
  854.         }
  855.         if (t.Expired())                // if max wait time has elapsed
  856.         {
  857.             rval = ESC_PRESSED;
  858.             break;
  859.         }
  860.         if (MonitorCarrier && !port.Carrier())
  861.         {                               // if lost carrier & care
  862.             rval = NO_CARRIER;
  863.             break;
  864.         }
  865.     }
  866.     delete buf;
  867.     return rval;
  868. }
  869.  
  870. /////////////////////////////////////////////////
  871. //  vDisplay (1 of 3)                          //
  872. //://////////////////////////////////////////////
  873. void vDisplay(char *Format, ...)
  874. {
  875.     va_list arg_ptr;
  876.     va_start(arg_ptr, Format);
  877.     vsprintf(vDispBuf, Format, arg_ptr);
  878.     Display(vDispBuf);
  879. }
  880.  
  881. /////////////////////////////////////////////////
  882. //  vDisplay (2 of 3)                          //
  883. //://////////////////////////////////////////////
  884. void vDisplay(int Color, char *Format, ...)
  885. {
  886.     va_list arg_ptr;
  887.     va_start(arg_ptr, Format);
  888.     vsprintf(vDispBuf, Format, arg_ptr);
  889.     Display(Color, vDispBuf);
  890. }
  891.  
  892. /////////////////////////////////////////////////
  893. //  vDisplay (3 of 3)                          //
  894. //://////////////////////////////////////////////
  895. void vDisplay(int Row, int Col, char *Format, ...)
  896. {
  897.     va_list arg_ptr;
  898.     va_start(arg_ptr, Format);
  899.     vsprintf(vDispBuf, Format, arg_ptr);
  900.     Display(Row, Col, vDispBuf);
  901. }
  902.  
  903. /////////////////////////////////////////////////
  904. //  PushScreen                                 //
  905. //://////////////////////////////////////////////
  906. int PushScreen(int Row, int Col, int NbrRows, int NbrCols)
  907. {
  908.     if (ScreenIndex == 10)              // return if ScreensArray is full
  909.         return (-1);
  910.     if ((ScreensArray[ScreenIndex] = new char[2*NbrRows*NbrCols+16]) == NULL)
  911.         return (-2);                    // return if can't alloc screen buffer
  912.     pu_scrnd(Row, Col, NbrRows, NbrCols, ScreensArray[ScreenIndex]);
  913.     ++ScreenIndex;                      // increment the index
  914.     return 0;
  915. }
  916.  
  917. /////////////////////////////////////////////////
  918. //  PopScreen                                  //
  919. //://////////////////////////////////////////////
  920. int PopScreen()
  921. {
  922.     if (ScreenIndex == 0)
  923.         return (-1);                    // return if no screens pushed
  924.     --ScreenIndex;
  925.     po_scrnd(ScreensArray[ScreenIndex]); // call low level restore screen
  926.     delete ScreensArray[ScreenIndex];    // release allocated memory
  927.     return 0;
  928. }
  929.  
  930.  
  931. /////////////////////////////////////////////////
  932. //  ReceiveXmodem                              //
  933. //://////////////////////////////////////////////
  934. void ReceiveXmodem(char *FileName)
  935. {
  936.     FILE *fh;
  937.     char *pc, *buf, *end;
  938.  
  939.     /////////////////////////////////////////////
  940.     //  alloc memory, open file, display info  //
  941.     /////////////////////////////////////////////
  942.     if ((buf = new char[1024 + 10]) == NULL) // alloc enough for 1K blocks
  943.     {
  944.         Prompt((char *)&buf, "Not enough memory, Press ENTER\a", 0);
  945.         return;                         // abort if can't alloc memory
  946.     }
  947.     if ((fh = fopen(FileName, "rb")) != NULL)
  948.     {
  949.         Prompt((char *)&buf, "File exists, Press ENTER\a", 0);
  950.         delete buf;
  951.         return;                         // abort if file exists
  952.     }
  953.     if ((fh = fopen(FileName, "wb")) == NULL)
  954.     {
  955.         Prompt((char *)&buf, "File creation error, Press ENTER\a", 0);
  956.         delete buf;
  957.         return;                         // abort if can't open output file
  958.     }
  959.  
  960.     ///////////////////////////////
  961.     //  pull up transfer window  //
  962.     ///////////////////////////////
  963.     TransferWindow(-1);                 // display receive transfer window
  964.     Display(1, 48, strupr(FileName));
  965.  
  966.     //////////////////////////////////////////////////////
  967.     //  set xflow off, params to N81, ignore bad chars  //
  968.     //////////////////////////////////////////////////////
  969.     char save_xoff, save_flushbad, save_params[10];
  970.     SetXmodemMode(save_xoff, save_flushbad, save_params);
  971.  
  972.     //////////////////////////////
  973.     //  intitiate the transfer  //
  974.     //////////////////////////////
  975.     static int BlockSizeAry[] = { 0, 128, 1024 };  // nul, SOH, STX
  976.     int rval, LongCh, TimeOuts, BlockSize, UseCrc;
  977.     int i, BlockNbr, RxdBlockNbr, Checksum, NeedHdrChar, ConsecErrors;
  978.     long BytesTransferred = 0L;
  979.  
  980.     TimeOuts = 0;
  981.     rval = 1;
  982.     while (rval == 1)
  983.     {
  984.         port.Tx((TimeOuts < 6) ? 'C' : NAK);    // try CRC Xmodem first, if
  985.         switch (LongCh = GetHdrChar(90))        // no response try checksum
  986.         {
  987.           case SOH:                     // 128 byte block header
  988.           case STX:                     // 1K byte block header
  989.             BlockSize = BlockSizeAry[LongCh];
  990.             rval = 0;
  991.             break;
  992.           case TIMED_OUT:               // no response
  993.             if (++TimeOuts > 10)
  994.                 rval = TIMED_OUT;
  995.             break;
  996.           default:                      // fatal error
  997.             rval = LongCh;
  998.             break;
  999.         }
  1000.     }
  1001.     UseCrc = (TimeOuts < 5) ? 1 : 0;    // set type of checksum to use
  1002.     BlockNbr = 1;                       // initial block number
  1003.     NeedHdrChar = TimeOuts = ConsecErrors = 0;
  1004.  
  1005.     while (!rval)
  1006.     {
  1007.         LongCh = 0;
  1008.         while (!LongCh)                 // while good blocks
  1009.         {
  1010.             ////////////////////////////////
  1011.             //  get the next header char  //
  1012.             ////////////////////////////////
  1013.             if (NeedHdrChar)            // if haven't already got SOH
  1014.             {
  1015.                 LongCh = GetHdrChar(180);   // wait up to 10 secs
  1016.                 if ((char)LongCh != SOH && (char)LongCh != STX)
  1017.                     break;
  1018.                 TimeOuts = 0;
  1019.                 BlockSize = BlockSizeAry[LongCh];
  1020.             }
  1021.             else
  1022.                 NeedHdrChar = 1;
  1023.             ////////////////////////////
  1024.             //  get the block number  //
  1025.             ////////////////////////////
  1026.             if ((LongCh = RxWithTimeOut(90)) < 0)   // 90 == 5 secs
  1027.                 break;
  1028.             RxdBlockNbr = LongCh;
  1029.             if ((LongCh = RxWithTimeOut(90)) < 0)
  1030.                 break;
  1031.             if (LongCh + RxdBlockNbr != 0xff)
  1032.             {                               // block complement error
  1033.                 LongCh = BLK_COMP_ERR;
  1034.                 break;
  1035.             }
  1036.             if ((char)RxdBlockNbr != (char)BlockNbr)
  1037.             {                               // not expected block number
  1038.                 LongCh = BLK_SEQ_ERR;
  1039.                 break;
  1040.             }
  1041.             ////////////////////////
  1042.             //  receive the data  //
  1043.             ////////////////////////
  1044.             Checksum = 0;
  1045.             pc = buf, end = pc + BlockSize; // initialize variables
  1046.             while (pc < end)
  1047.             {
  1048.                 if ((LongCh = RxWithTimeOut(90)) < 0)
  1049.                     break;
  1050.                 *pc++ = (char)LongCh;       // read and store data
  1051.                 if (UseCrc)                 // update crc if using crc
  1052.                     Checksum = update_crc(Checksum, (char)LongCh);
  1053.                 else
  1054.                     Checksum += LongCh;     // else update simple checksum
  1055.             }
  1056.             if (pc != end)                  // if an error occurred while
  1057.                 break;                      //  receiving block data
  1058.             ///////////////////////////////
  1059.             //  receive the check value  //
  1060.             ///////////////////////////////
  1061.             if ((LongCh = RxWithTimeOut(90)) < 0)
  1062.                 break;                  // high byte of crc or the checksum
  1063.             if (UseCrc)
  1064.             {
  1065.                 if ((Checksum ^= (LongCh << 8)) & 0xff00)
  1066.                 {                          // crc didn't match, breaking here
  1067.                     LongCh = CHECKSUM_ERR; // makes error recovery faster on
  1068.                     break;                 // short blocks
  1069.                 }
  1070.                 if ((LongCh = RxWithTimeOut(90)) < 0)
  1071.                     break;              // low byte of crc
  1072.             }
  1073.             if ((LongCh ^= Checksum) != 0)
  1074.             {                           // crc or checksum didn't match
  1075.                 LongCh = CHECKSUM_ERR;
  1076.                 break;
  1077.             }
  1078.             port.Tx(ACK);                   // good block, send an ACK
  1079.             fwrite(buf, 1, BlockSize, fh);  // update the file
  1080.             ++BlockNbr;                     // advance the blocknbr
  1081.             BytesTransferred += (long)BlockSize;
  1082.             vDisplay(3, 48, "%ld", BytesTransferred);
  1083.             ConsecErrors = 0;               // reset consecutive error count
  1084.         }
  1085.         ///////////////////////////////
  1086.         //  errors and EOT handling  //
  1087.         ///////////////////////////////
  1088.         if (LongCh == EOT)                  // end of file indicator
  1089.         {
  1090.             for (i = 0; i < 3; i++)
  1091.             {
  1092.                 port.Tx(NAK);               // try to confirm real EOT
  1093.                 if ((char)(LongCh = GetHdrChar(90)) == EOT)
  1094.                 {
  1095.                     port.Tx(ACK);           // if got 2nd EOT after NAKing
  1096.                     rval = 1;               // 1st one, it must be real EOT
  1097.                     break;
  1098.                 }
  1099.                 if ((char)LongCh == SOH     // if got an SOH or STX, 1st EOT
  1100.                  || (char)LongCh == STX     // must have been false
  1101.                  || (LongCh < 0 && LongCh != TIMED_OUT))
  1102.                     break;                  // if < 0, fatal error occurred
  1103.             }
  1104.             if (LongCh == TIMED_OUT)        // got an EOT but after 3 attempts
  1105.                 rval = 2;                   // to get confirmation, timed out,
  1106.             if (rval)                       // so exit at different level
  1107.                 break;
  1108.  
  1109.             if ((char)LongCh == SOH || (char)LongCh == STX)
  1110.             {                                       // got EOT followed by
  1111.                 Display(5, 48, "False EOT");        // valid start of block
  1112.                 BlockSize = BlockSizeAry[LongCh];   // char, try to get
  1113.                 NeedHdrChar = 0;                    // another block
  1114.                 continue;
  1115.             }
  1116.         }
  1117.         if (++ConsecErrors == 10)       // exit after 10 errors
  1118.             LongCh = ERROR_LIMIT;
  1119.         vDisplay(4, 48, "%d", ConsecErrors);
  1120.         switch (LongCh)                 // switch on error code
  1121.         {
  1122.           case BLK_COMP_ERR:
  1123.             Display(5, 48, "Bad Block Complement");   //  complement error
  1124.             if ((rval = WaitForBlockToEnd(18)) != 0)  // wait for port to dry
  1125.                 break;                                // up, send a NAK, and
  1126.             port.Tx(NAK);                             // and try again
  1127.             break;
  1128.           case BLK_SEQ_ERR:
  1129.             Display("Bad Block Sequence  ");
  1130.             if ((char)RxdBlockNbr != (char)(BlockNbr - 1)
  1131.              || (LongCh = WaitForBlockToEnd(18)) != 0)
  1132.             {                                         //  sequence error
  1133.                 rval = LongCh;                        // if wrong seq and not
  1134.                 break;                                // previous seq either,
  1135.             }                                         // then have fatal error.
  1136.             port.Tx(ACK);                             // if got repeat block,
  1137.             continue;                                 // ACK it and try again
  1138.           case CHECKSUM_ERR:
  1139.             Display(5, 48, (UseCrc) ?                 //  bad crc or checksum
  1140.              "Block Crc Error     " :
  1141.              "Block Checksum Error");
  1142.             if ((rval = WaitForBlockToEnd(4)) != 0)   // wait for about 1/4
  1143.                 break;                                // second of silence,
  1144.             port.Tx(NAK);                             // then NAK block and
  1145.             break;                                    // try again
  1146.           case TIMED_OUT:
  1147.             Display(5, 48,                  //  remote not answering
  1148.              "Timed Out           ");
  1149.             port.Tx(NAK);                   // if no answer, send a NAK to
  1150.             break;                          // try to get a response
  1151.           default:
  1152.             rval = LongCh;                  //   CAN, ESC, lost carrier, ...
  1153.             break;                          // set exit code and quit
  1154.         }
  1155.     }
  1156.     if (rval == ESC_PRESSED)                // send CANs if ESC pressed
  1157.     {
  1158.         Display(5, 48, "Aborting File Transfer ...");
  1159.         WaitForBlockToEnd(18);
  1160.         SendCANs();
  1161.     }
  1162.     fclose(fh);                             // close the file, free the
  1163.     delete(buf);                            // memory, reset params and exit
  1164.     ResetXmodemMode(save_xoff, save_flushbad, save_params);
  1165.     TransferWindow(0);
  1166.     XmodemExitMsg(rval);                    // display exit message
  1167. }
  1168.  
  1169. /////////////////////////////////////////////////
  1170. //  SendXmodem                                 //
  1171. //://////////////////////////////////////////////
  1172. void SendXmodem(char *FileName, int BlockSize)
  1173. {
  1174.     FILE *fh;
  1175.     char *pc, *buf, *end;
  1176.     long FileLength;
  1177.  
  1178.     /////////////////////////////////////////////
  1179.     //  alloc memory, open file, display info  //
  1180.     /////////////////////////////////////////////
  1181. #if defined(__ZTC__)
  1182.     FileLength = filesize(FileName);    // Zortech's filesize
  1183. #endif
  1184.     if ((buf = new char[BlockSize + 10]) == NULL)
  1185.     {
  1186.         Prompt((char *)&buf, "Not enough memory, Press ENTER\a", 0);
  1187.         return;                         // abort if can't alloc memory
  1188.     }
  1189.     if ((fh = fopen(FileName, "rb")) == NULL)
  1190.     {
  1191.         Prompt((char *)&buf, "File not found, Press ENTER\a", 0);
  1192.         delete buf;
  1193.         return;                         // abort if can't open file
  1194.     }
  1195. #if !defined(__ZTC__)
  1196.     FileLength = filelength(fileno(fh));
  1197. #endif
  1198.  
  1199.     ///////////////////////////////
  1200.     //  pull up transfer window  //
  1201.     ///////////////////////////////
  1202.     TransferWindow(1);                  // display transmit transfer window
  1203.     Display(1, 48, strupr(FileName));
  1204.     vDisplay(2, 48, "%ld", FileLength); // display filename and size
  1205.  
  1206.     //////////////////////////////////////////////////////
  1207.     //  set xflow off, params to N81, ignore bad chars  //
  1208.     //////////////////////////////////////////////////////
  1209.     char save_xoff, save_flushbad, save_params[10];
  1210.     SetXmodemMode(save_xoff, save_flushbad, save_params);
  1211.  
  1212.     /////////////////////////////
  1213.     //  wait for a 'C' or NAK  //
  1214.     /////////////////////////////
  1215.     int LongCh, rval, CANsRxd, TimeOuts, UseCrc;
  1216.  
  1217.     CANsRxd = TimeOuts = UseCrc = 0;
  1218.     rval = 1;
  1219.     while (rval == 1)                   // wait for something to happen
  1220.     {
  1221.         switch (LongCh = RxWithTimeOut(18))
  1222.         {
  1223.           case 'C':                     // CRC xmodem
  1224.               UseCrc = 1;
  1225.               rval = 0;
  1226.               break;
  1227.           case NAK:                     // simple checksum xmodem
  1228.               rval = 0;
  1229.               break;
  1230.           case TIMED_OUT:               // didn't get anything
  1231.               if (++TimeOuts < 60)
  1232.                   continue;
  1233.               rval = LongCh;
  1234.               break;
  1235.           case CAN:                     // remote sent control-X's
  1236.               if (++CANsRxd < 2)
  1237.                   continue;
  1238.                 rval = GOT_CANNED;
  1239.                 break;
  1240.           case NO_CARRIER:              // lost the carrier
  1241.           case ESC_PRESSED:             // cancelled on this end
  1242.               rval = LongCh;
  1243.               break;
  1244.           default:
  1245.               CANsRxd = 0;
  1246.               break;
  1247.         }
  1248.     }
  1249.  
  1250.     ////////////////////////////
  1251.     //  send the file blocks  //
  1252.     ////////////////////////////
  1253.     char BlockNbr = '\0', HdrChar = (BlockSize == 1024) ? STX : SOH;
  1254.     int i, ConsecErrors, BadBlock, FirstAck, Checksum, ExtraNAK;
  1255.     long BytesTransferred = 0L;
  1256.  
  1257.     BadBlock = FirstAck = CANsRxd = 0;
  1258.     while (!rval)
  1259.     {
  1260.         if (!BadBlock)              // if last block was OK
  1261.         {
  1262.             ++BlockNbr;
  1263.             ConsecErrors = 0;
  1264.             i = fread(buf, 1, BlockSize, fh);   // read a block in
  1265.             if (i == 0)             // if end of file
  1266.             {
  1267.                 rval = 1;           // exit value if EOT gets ACKd
  1268.                 while (1)
  1269.                 {
  1270.                     port.Tx(EOT);
  1271.                     if ((char)RxWithTimeOut(18) == ACK)
  1272.                         break;
  1273.                     if (i++ == 4)
  1274.                     {
  1275.                         rval = 2;   // exit value if EOT doesn't get ACKd
  1276.                         break;
  1277.                     }
  1278.                 }
  1279.                 break;
  1280.             }
  1281.             if (i < BlockSize)          // pad last block with CTL-Z's
  1282.                 memset(&buf[i], '\x1a', BlockSize - i);
  1283.         }
  1284.         else                            // had a bad block
  1285.         {
  1286.             if (++ConsecErrors == 10)
  1287.             {                           // 10 consecutive errors, abort
  1288.                 rval = ERROR_LIMIT;
  1289.                 continue;
  1290.             }
  1291.             vDisplay(4, 48, "%d", ConsecErrors);
  1292.             vDisplay(5, 48, "Got NAK for block %d", BlockNbr);
  1293.             if (!FirstAck)              // if not any good blocks yet
  1294.             {
  1295.                 if (BlockSize == 1024 && ConsecErrors == 2)
  1296.                 {
  1297.                     BlockSize = 128;    // Xmodem-1k isn't working,
  1298.                     BadBlock = 0;       // try 128 byte blocks
  1299.                     BlockNbr = '\0';
  1300.                     HdrChar = SOH;
  1301.                     rewind(fh);
  1302.                     continue;
  1303.                 }
  1304.                 if (UseCrc && ConsecErrors == 8)
  1305.                     UseCrc = 0;         // last ditch effort, try checksum
  1306.             }
  1307.         }
  1308.         port.Tx(HdrChar);               // send block header
  1309.         port.Tx(BlockNbr);
  1310.         port.Tx(~BlockNbr);
  1311.         port.Tx(buf, BlockSize);        // send file data
  1312.         if (UseCrc)
  1313.         {                               // send the CRC
  1314.             Checksum = calc_crc(buf, BlockSize);
  1315.             port.Tx((char)(Checksum >> 8));
  1316.             port.Tx((char)Checksum);
  1317.         }
  1318.         else
  1319.         {                               // if not CRC, send simple checksum
  1320.             Checksum = 0;
  1321.             pc = buf, end = pc + BlockSize;
  1322.             while (pc < end)
  1323.                 Checksum += (int)*pc++;
  1324.             port.Tx(Checksum);
  1325.         }
  1326.         ////////////////////
  1327.         //  wait for ACK  //
  1328.         ////////////////////
  1329.         ExtraNAK = 0;                    //---------------------------//
  1330.         while (!rval && !port.TxEmpty()) // while block is being sent //
  1331.         {                                //                           //
  1332.             if (port.RxLevel() != 0)     //  if something comes in    //
  1333.             {                            //                           //
  1334.                 LongCh = port.Rx();      //  get whatever it is       //
  1335.                                          //---------------------------//
  1336.                 //--------------------------------------------------//
  1337.                 //   If it's an ACK ignore it.  It may be due to a  //
  1338.                 // duplicate packet error but it is safer to make   //
  1339.                 // the receiver timeout and send another ACK after  //
  1340.                 // the packet has been sent.                        //
  1341.                 //   If it's a CAN, ignore that also and make them  //
  1342.                 // resend them at the end of the block.             //
  1343.                 //   An out of sequence NAK may be due to a block   //
  1344.                 // complement error.  Go ahead and take it but wait //
  1345.                 // for a few seconds after the block has finished   //
  1346.                 // transmitting to accept it as valid.              //
  1347.                 //--------------------------------------------------//
  1348.                 if ((char)LongCh == NAK)
  1349.                     ExtraNAK = 1;
  1350.             }
  1351.             if (KBHIT && KBREAD == X_ESC)   // monitor the keyboard
  1352.                 rval = ESC_PRESSED;
  1353.             if (!port.Carrier())            // monitor the carrier
  1354.                 rval = NO_CARRIER;
  1355.         }
  1356.         TimeOuts = 0;   //-------------------------------------------//
  1357.         while (!rval)   //  transmit buffer empty now, block is sent //
  1358.         {               //-------------------------------------------//
  1359.             LongCh = RxWithTimeOut(18);     // receive with 1 sec timeout
  1360.             if (LongCh >= 0)
  1361.             {
  1362.                 if ((char)LongCh == ACK)    // if got an ACK, have a good
  1363.                 {                           // block
  1364.                     FirstAck = 1;
  1365.                     BadBlock = 0;
  1366.                     BytesTransferred += (long)BlockSize;
  1367.                     vDisplay(3, 48, "%ld", BytesTransferred);
  1368.                     break;
  1369.                 }
  1370.                 if ((char)LongCh == NAK)    // if got a NAK, have a bad
  1371.                 {                           // block
  1372.                     BadBlock = 1;
  1373.                     break;
  1374.                 }
  1375.                 if ((char)LongCh == CAN && ++CANsRxd >= 2)
  1376.                     rval = GOT_CANNED;      // remote cancelled transfer
  1377.                 else
  1378.                     CANsRxd = 0;
  1379.                 continue;
  1380.             }
  1381.             if (LongCh == TIMED_OUT)
  1382.             {
  1383.                 if (++TimeOuts == 3 && ExtraNAK)
  1384.                 {                       // If had gotten a NAK during the //
  1385.                     BadBlock = 1;       // transmission of the block and  //
  1386.                     break;              // 3 secs have passed with no     //
  1387.                 }                       // chars, assume NAK was genuine  //
  1388.                 else if (TimeOuts >= 45)
  1389.                     rval = LongCh;      // if nothing after 45 secs, abort
  1390.             }
  1391.             else if (LongCh < 0)        // LongCh < 0 but not a timeout is
  1392.                 rval = LongCh;          // is a fatal error
  1393.         }
  1394.     }
  1395.     //////////////////////////////////////////////////////
  1396.     //  close file, free memory, reset params, & exit   //
  1397.     //////////////////////////////////////////////////////
  1398.     fclose(fh);
  1399.     delete buf;
  1400.     if (rval == ESC_PRESSED)
  1401.     {
  1402.         Display(5, 48, "Aborting file transfer ...");
  1403.         while (port.Carrier() && !port.TxEmpty())
  1404.             ;
  1405.         SendCANs();
  1406.     }
  1407.     ResetXmodemMode(save_xoff, save_flushbad, save_params);
  1408.     TransferWindow(0);
  1409.     XmodemExitMsg(rval);
  1410. }
  1411.  
  1412. /////////////////////////////////////////////////
  1413. //  XmodemExitMsg                              //
  1414. //://////////////////////////////////////////////
  1415. void XmodemExitMsg(int rval)
  1416. {
  1417.     switch (rval)                           // display exit message
  1418.     {
  1419.       case 1:
  1420.         Display("\nFile transferred\n");
  1421.         break;
  1422.       case 2:
  1423.         Display("\nFile transferred, EOT unconfirmed\n");
  1424.         break;
  1425.       case TIMED_OUT:
  1426.         Display("\nRemote fails to respond\n");
  1427.         break;
  1428.       case GOT_CANNED:
  1429.         Display("\nRemote aborted transfer\n");
  1430.         break;
  1431.       case ERROR_LIMIT:
  1432.         Display("\nMaximum error count exceeded\n");
  1433.         break;
  1434.       case ESC_PRESSED:
  1435.         Display("File transfer aborted\n");
  1436.         break;
  1437.       case NO_CARRIER:
  1438.         Display("\nCarrier lost\n");
  1439.         break;
  1440.       default:
  1441.         Display("\nUnknown exit code\n");
  1442.         break;
  1443.     }
  1444. }
  1445.  
  1446. /////////////////////////////////////////////////
  1447. //  RxWithTimeOut                              //
  1448. //://////////////////////////////////////////////
  1449. int RxWithTimeOut(int Ticks)
  1450. {
  1451.     int LongCh;
  1452.  
  1453.     if (!(B_RXEMPTY & (LongCh = port.Rx())))
  1454.         return (LongCh & 0xff);         // fast return if char available
  1455.  
  1456.     Timer t(Ticks);                     // start timeout timer
  1457.     while (1)
  1458.     {
  1459.         if (port.RxLevel())
  1460.             return (port.Rx() & 0xff);  // return char if available
  1461.         if (!port.Carrier())
  1462.             return NO_CARRIER;          // return if carrier was lost
  1463.         if (KBHIT && KBREAD == X_ESC)
  1464.             return ESC_PRESSED;         // return if ESC was pressed
  1465.         if (t.Expired())
  1466.             return TIMED_OUT;           // return if Ticks have expierd
  1467.     }
  1468. }
  1469.  
  1470. /////////////////////////////////////////////////
  1471. //  SetXmodemMode                              //
  1472. //://////////////////////////////////////////////
  1473. void SetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params)
  1474. {
  1475.     char new_params[10], *pc;
  1476.  
  1477.     save_xoff = port.XFlow();           // make sure XON/XOFF is turned off
  1478.     port.XFlow('\0');
  1479.     strcpy(save_params, port.Params()); // make sure params are N,8,1
  1480.     strcpy(new_params, save_params);
  1481.     for (pc = new_params; isdigit(*pc); pc++)
  1482.         ;                               // skip over baudrate
  1483.     strcpy(pc,"N81");                   // new_params = baudrate+"N81"
  1484.     port.Params(new_params);
  1485.     DisplayParameters();
  1486.     port.TxFlush();                     // flush the transmit side only
  1487.     save_flushbad = port.FlushBadChars();
  1488.     port.FlushBadChars(1);              // flush framing errors mode on
  1489. }
  1490.  
  1491. /////////////////////////////////////////////////
  1492. //  ResetXmodemMode                            //
  1493. //://////////////////////////////////////////////
  1494. void ResetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params)
  1495. {
  1496.     port.XFlow(save_xoff);                  // reset to original XOFF mode
  1497.     port.FlushBadChars(save_flushbad);      // reset flush bad chars switch
  1498.     port.Params(save_params);               // back to original parameters
  1499.     DisplayParameters();
  1500. }
  1501.  
  1502. /////////////////////////////////////////////////
  1503. //  SendCANs                                   //
  1504. //://////////////////////////////////////////////
  1505. void SendCANs()
  1506. {
  1507.     port.Tx("\x18\x18\x18\x18\x18\b\b\b\b\b");  // send a bunch of control-X's
  1508.     while (port.Carrier() && !port.TxEmpty())
  1509.         ;
  1510.     port.RxFlush();
  1511. }
  1512.  
  1513. /////////////////////////////////////////////////
  1514. //  WaitForBlockToEnd                          //
  1515. //://////////////////////////////////////////////
  1516. int WaitForBlockToEnd(int Ticks)
  1517. {
  1518.     int LongCh;
  1519.  
  1520.     while ((LongCh = RxWithTimeOut(Ticks)) >= 0)
  1521.         ;                                       // wait for timeout or error
  1522.     return (LongCh == TIMED_OUT) ? 0 : LongCh;
  1523. }
  1524.  
  1525. /////////////////////////////////////////////////
  1526. //  GetHdrChar                                 //
  1527. //://////////////////////////////////////////////
  1528. int GetHdrChar(int Ticks)
  1529. {
  1530.     int LongCh, TimeOuts = 0, CANsRxd = 0;
  1531.  
  1532.     while (1)
  1533.     {
  1534.         LongCh = RxWithTimeOut(1);
  1535.         if ((char)LongCh == SOH || (char)LongCh == STX || (char)LongCh == EOT)
  1536.             break;                  // got block header
  1537.         if (LongCh == TIMED_OUT)
  1538.         {
  1539.             if (++TimeOuts > Ticks)
  1540.                 break;              // exceeded max Ticks
  1541.         }
  1542.         else if (LongCh < 0)
  1543.             break;                  // aborted, lost carrier, ...
  1544.         if ((char)LongCh == CAN && ++CANsRxd >= 2)
  1545.         {
  1546.             LongCh = GOT_CANNED;
  1547.             break;                  // remote cancelled
  1548.         }
  1549.         else
  1550.             CANsRxd = 0;            // garbage, reset CAN counter
  1551.     }
  1552.     return (LongCh);
  1553. }
  1554.  
  1555. /////////////////////////////////////////////////
  1556. //  TransferWindow                             //
  1557. //://////////////////////////////////////////////
  1558. void TransferWindow(int Flag)
  1559. {
  1560.     if (Flag == 0)
  1561.     {
  1562.         RemoveBox();
  1563.         return;
  1564.     }
  1565.     DrawBox(0, 26, 7, 53);
  1566.     v_color = ylw;
  1567.     if (Flag > 0)
  1568.         Display(1, 34, "Transmitting:");
  1569.     else
  1570.         Display(1, 37, "Receiving:");
  1571.     Display(2, 35, "Total Bytes:");
  1572.     Display(3, 29, "Bytes Transferred:");
  1573.     Display(4, 28, "Consecutive Errors:");
  1574.     Display(5, 36, "Last Error:");
  1575.     v_color = wht;
  1576. }
  1577.  
  1578.